﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Media.Animation;
using TheGameClient.Processes;
using TheGameClient.Webservices;
using Waaahsabi.Controls.TheGame;

namespace TheGameClient
{
	/// <summary>
	/// This is one of the earliest implementations of a UI for the game. Certain ways of implementing event handlers
	/// have changed since (no more using local eventhandler variables etc)
	/// Thus some parts of this may seem awkward compared to the rest.
	/// </summary>
	public partial class GenericSystemInterface
	{
		private readonly ObservableCollection<GenericSelectorButton> _featureButtons;
		private readonly ObservableCollection<GenericSelectorButton> _securityButtons;
		private readonly List<ISecuritySystem> _securitySystems;
		private readonly List<ISystemFeature> _systemFeatures;
		private bool _isLoggedIn;

		private bool _isTraceActive;
		private LastListType _lastListType;

		public GenericSystemInterface()
		{
			// Required to initialize variables
			InitializeComponent();
			_securitySystems = new List<ISecuritySystem>();
			_securityButtons = new ObservableCollection<GenericSelectorButton>();
			_systemFeatures = new List<ISystemFeature>();
			_featureButtons = new ObservableCollection<GenericSelectorButton>();
		}

		public ulong SystemId { get; set; }
		public Guid ConnectionId { get; set; }
		public event EventHandler Disconnect;

		public void Initialize(ulong systemId, Guid connectionId, string welcomeTitle, string motd)
		{
			if (connectionId == Guid.Empty || systemId == 0)
				throw new ArgumentException();

			App.Client.TraceReceived += ClientTraceReceived;
			App.Client.TraceEndedReceived += ClientTraceEnded;
			ConnectionId = connectionId;
			SystemId = systemId;
			welcomeScreen.WelcomeTitle = welcomeTitle;
			welcomeScreen.WelcomeMessage = motd;

			welcomeScreen.DisconnectClicked += delegate
			{
				App.Client.Connect_DisconnectFromSystemCompleted += ClientConnectDisconnectFromSystemCompleted;
				App.Client.Connect_DisconnectFromSystemAsync(App.InternalSessionId, ConnectionId);
				if (Disconnect != null) Disconnect(this, null);
			};

			welcomeScreen.ContinueClicked += delegate { InitializeSubsystems(); };

			btnBack.Click += delegate
			{
				switch (_lastListType)
				{
				case LastListType.Feature:
					ShowFeatureList();
					break;
				case LastListType.Security:
					ShowSecsysList();
					break;
				}
			};
		}

		private void ClientConnectDisconnectFromSystemCompleted(object sender,
		                                                          Connect_DisconnectFromSystemCompletedEventArgs e)
		{
			App.Client.Connect_DisconnectFromSystemCompleted -= ClientConnectDisconnectFromSystemCompleted;
			App.Client.TraceReceived -= ClientTraceReceived;
			App.Client.TraceEndedReceived -= ClientTraceEnded;
			if (e.Error != null)
			{
				App.ServerError();
				return;
			}
			if (e.Result == false)
			{
				MessageBox.Show(string.Format("Error occured during disconnect from {0}.", welcomeScreen.WelcomeTitle));
			}
		}

		private void ClientTraceEnded(object sender, TraceEndedReceivedEventArgs e)
		{
			App.Client.TraceEndedReceived -= ClientTraceEnded;
			if (e.Error != null)
			{
				App.ServerError();
				return;
			}
			App.Client.Connect_DisconnectFromSystemCompleted += ClientConnectDisconnectFromSystemCompleted;
			App.Client.Connect_DisconnectFromSystemAsync(App.InternalSessionId, ConnectionId);
			if (Disconnect != null)
				Disconnect(this, null);
		}

		private void ClientTraceReceived(object sender, TraceReceivedEventArgs e)
		{
			if (e.Error != null)
			{
				App.ServerError();
				return;
			}
			if (e.ConnectionId != ConnectionId)
				return;
			if (!_isTraceActive)
			{
				VisualStateManager.GoToState(this, "TraceRunning", false);
				FlashStatusBar.Begin();
				FlashProgressBar.Begin();
				traceViewItem.InitialTime = e.RemainingMilliSeconds;
				_isTraceActive = true;
				return;
			}
			traceViewItem.UpdateStatus(e.RemainingMilliSeconds);
			if (e.RemainingMilliSeconds == 0)
			{
				App.Client.TraceReceived -= ClientTraceReceived;
				FlashProgressBar.Stop();
				FlashStatusBar.Stop();
			}
			CreateAndPlayTraceAnimation();
		}

		private void CreateAndPlayTraceAnimation()
		{
			TimedDistortEffect effect = new TimedDistortEffect();
			LayoutRoot.Effect = effect;
			Storyboard animation = new Storyboard();
			Random rnd = new Random(Environment.TickCount);
			DoubleAnimationUsingKeyFrames waveStrength = new DoubleAnimationUsingKeyFrames();
			double peak = rnd.NextDouble()/10;
			waveStrength.KeyFrames.Add(new LinearDoubleKeyFrame
			{Value = 0.0, KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0))});
			waveStrength.KeyFrames.Add(new LinearDoubleKeyFrame
			{Value = peak, KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(100))});
			waveStrength.KeyFrames.Add(new LinearDoubleKeyFrame
			{Value = 0.0, KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(500))});
			Storyboard.SetTargetProperty(waveStrength, new PropertyPath(TimedDistortEffect.WaveStrengthProperty));
			Storyboard.SetTarget(waveStrength, effect);
			animation.Children.Add(waveStrength);

			DoubleAnimationUsingKeyFrames frequency = new DoubleAnimationUsingKeyFrames();
			peak = rnd.NextDouble()*10;
			frequency.KeyFrames.Add(new LinearDoubleKeyFrame
			{Value = 0.0, KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0))});
			frequency.KeyFrames.Add(new LinearDoubleKeyFrame
			{Value = peak, KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(100))});
			frequency.KeyFrames.Add(new LinearDoubleKeyFrame
			{Value = 0.0, KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(500))});
			Storyboard.SetTargetProperty(frequency, new PropertyPath(TimedDistortEffect.FrequencyProperty));
			Storyboard.SetTarget(frequency, effect);
			animation.Children.Add(frequency);

			EventHandler handler = null;
			handler = delegate
			{
				animation.Completed -= handler;
				LayoutRoot.Effect = null;
			};
			animation.Completed += handler;
			animation.Begin();
		}

		private void InitializeSubsystems()
		{
			if (_isLoggedIn)
			{
				_securitySystems.Clear();
				_securityButtons.Clear();
			}

			#region get features from service

			TheGameServiceClient client = App.Client;
			client.Connect_GetConnectedHostFeaturesCompleted += ClientConnectGetConnectedHostFeaturesCompleted;
			client.Connect_GetConnectedHostFeaturesAsync(App.InternalSessionId, ConnectionId);

			#endregion
		}

		private void ClientConnectGetConnectedHostFeaturesCompleted(object sender,
		                                                              Connect_GetConnectedHostFeaturesCompletedEventArgs e)
		{
			App.Client.Connect_GetConnectedHostFeaturesCompleted -= ClientConnectGetConnectedHostFeaturesCompleted;
			if (e.Error != null)
			{
				App.ServerError();
				return;
			}
			if (e.Result != null)
			{
				foreach (ComputerSystemFeature csf in e.Result)
				{
					IProcess process = MasterUiFactory.Instance.CreateUI(csf.Id);
					if (process is ISecuritySystem)
						_securitySystems.Add(process as ISecuritySystem);
					if (process is ISystemFeature)
						_systemFeatures.Add(process as ISystemFeature);
				}

				foreach (ISecuritySystem sec in _securitySystems)
				{
					sec.AccessAttempt += OnSecSysAccessAttempt;
					sec.BypassAttempt += OnSecSysBypassAttempt;
					GenericSelectorButton btn = new GenericSelectorButton
					{ItemName = sec.ProcessName, ItemInterfaceId = sec.InterfaceId};
					btn.Click += SecuritySystemSelect;
					_securityButtons.Add(btn);
				}
				securityList.Items = _securityButtons;

				foreach (ISystemFeature feature in _systemFeatures)
				{
					GenericSelectorButton btn = new GenericSelectorButton
					{ItemName = feature.ProcessName, ItemInterfaceId = feature.InterfaceId};
					btn.Click += FeatureSelect;
					_featureButtons.Add(btn);
				}
				featureList.Items = _featureButtons;
			}
			if (_securitySystems.Count > 0)
				ShowSecsysList();
			else
				ShowFeatureList();
		}

		private void FeatureSelect(object sender, RoutedEventArgs e)
		{
			if (sender is GenericSelectorButton)
			{
				GenericSelectorButton btn = sender as GenericSelectorButton;
				UIElement feature =
					(from x in _systemFeatures where x.InterfaceId == btn.ItemInterfaceId select x).Cast<UIElement>().FirstOrDefault();
				ShowSubSystem(feature);
			}
		}

		private void SecuritySystemSelect(object sender, RoutedEventArgs e)
		{
			if (sender is GenericSelectorButton)
			{
				GenericSelectorButton btn = sender as GenericSelectorButton;
				UIElement feature =
					(from x in _securitySystems where x.InterfaceId == btn.ItemInterfaceId select x).Cast<UIElement>().FirstOrDefault();
				ShowSubSystem(feature);
			}
		}

		private void OnSecSysAccessAttempt(object sender, AccessAttemptEventArgs evt)
		{
			if (sender is ISecuritySystem)
			{
				ISecuritySystem secSys = sender as ISecuritySystem;
				EventHandler<Connect_LoginToSystemCompletedEventArgs> loginCompletedHandler = null;
				loginCompletedHandler = (s, e) =>
				{
					App.Client.Connect_LoginToSystemCompleted -= loginCompletedHandler;
					if (e.Error != null)
					{
						App.ServerError();
						return;
					}
					if (e.Result)
					{
						_isLoggedIn = true;
						InitializeSubsystems();
					}
				};

				EventHandler<Connect_LoginToSystemAllowedCompletedEventArgs> loginAllowedHandler = null;
				loginAllowedHandler += (s, e) =>
				{
					App.Client.Connect_LoginToSystemAllowedCompleted -= loginAllowedHandler;
					if (e.Error != null)
					{
						App.ServerError();
						return;
					}
					if (e.Result)
					{
						App.Client.Connect_LoginToSystemCompleted += loginCompletedHandler;
						App.Client.Connect_LoginToSystemAsync(App.InternalSessionId, ConnectionId);
					}
				};

				EventHandler<Connect_TryAccessCompletedEventArgs> tryAccessHandler = null;
				tryAccessHandler = (s, e) =>
				{
					App.Client.Connect_TryAccessCompleted -= tryAccessHandler;
					if (e.Error != null)
					{
						App.ServerError();
						return;
					}
					secSys.SetBypassResult(e.Result);
					App.Client.Connect_LoginToSystemAllowedCompleted += loginAllowedHandler;
					App.Client.Connect_LoginToSystemAllowedAsync(App.InternalSessionId, ConnectionId);
				};

				App.Client.Connect_TryAccessCompleted += tryAccessHandler;
				App.Client.Connect_TryAccessAsync(App.InternalSessionId, ConnectionId, secSys.InterfaceId, evt.Parameters);
			}
		}

		private void OnSecSysBypassAttempt(object sender, AccessAttemptEventArgs evt)
		{
			if (sender is ISecuritySystem)
			{
				ISecuritySystem secSys = sender as ISecuritySystem;
				IProgram bypasser = evt.Bypasser;
				List<object> parameters = new List<object> {SystemId, secSys.InterfaceId, ConnectionId};
				if (evt.Parameters != null)
					parameters.AddRange(evt.Parameters);

				EventHandler<Connect_LoginToSystemCompletedEventArgs> loginCompletedHandler = null;
				loginCompletedHandler = (s, e) =>
				{
					App.Client.Connect_LoginToSystemCompleted -= loginCompletedHandler;
					if (e.Error != null)
					{
						App.ServerError();
						return;
					}
					if (e.Result)
					{
						_isLoggedIn = true;
						InitializeSubsystems();
					}
				};

				EventHandler<Connect_LoginToSystemAllowedCompletedEventArgs> loginAllowedHandler = null;
				loginAllowedHandler = (s, e) =>
				{
					App.Client.Connect_LoginToSystemAllowedCompleted -= loginAllowedHandler;
					if (e.Error != null)
					{
						App.ServerError();
						return;
					}
					if (e.Result)
					{
						App.Client.Connect_LoginToSystemCompleted += loginCompletedHandler;
						App.Client.Connect_LoginToSystemAsync(App.InternalSessionId, ConnectionId);
					}
				};

				EventHandler<ExecuteProgramCompletedEventArgs> executeProgramHandler = null;
				executeProgramHandler = (s, e) =>
				{
					App.Client.ExecuteProgramCompleted -= executeProgramHandler;
					if (e.Error != null)
					{
						App.ServerError();
						return;
					}

					secSys.SetBypassResult(e.Result);
					bypasser.SetOperationResult(e.Result);
					if (e.Result)
					{
						App.Client.Connect_LoginToSystemAllowedCompleted += loginAllowedHandler;
						App.Client.Connect_LoginToSystemAllowedAsync(App.InternalSessionId, ConnectionId);
					}
				};
				App.Client.ExecuteProgramCompleted += executeProgramHandler;
				App.Client.ExecuteProgramAsync(App.InternalSessionId, bypasser.ProgramId, parameters);
			}
		}

		public void ShowWelcomeScreen()
		{
			VisualStateManager.GoToState(this, "Welcome", true);
		}

		public void ShowSecsysList()
		{
			VisualStateManager.GoToState(this, "SecurityList", true);
			_lastListType = LastListType.Security;
		}

		public void ShowFeatureList()
		{
			VisualStateManager.GoToState(this, "FeatureList", true);
			_lastListType = LastListType.Feature;
		}

		public void ShowSubSystem(UIElement contentElement)
		{
			if (contentElement == null)
				return;
			if (contentElement is IProcess)
				subSystemDisplay.Title = ((IProcess) contentElement).ProcessName;

			if (contentElement is ISystemFeature)
				((ISystemFeature) contentElement).Load(SystemId);

			subSystemDisplay.Content = contentElement;
			VisualStateManager.GoToState(this, "Subsystem", true);
		}

		public void Close()
		{
			App.Client.TraceReceived -= ClientTraceReceived;
			App.Client.TraceEndedReceived -= ClientTraceEnded;
			App.Client.Connect_DisconnectFromSystemCompleted -= ClientConnectDisconnectFromSystemCompleted;
			App.Client.Connect_GetConnectedHostFeaturesCompleted -= ClientConnectGetConnectedHostFeaturesCompleted;
		}
	}

	public enum LastListType
	{
		Security,
		Feature
	}
}